#include "parser.h"
#include <stdio.h>
#include <string>
#include <cctype>

using namespace std;

extern const size_t MAX_STRING_SIZE;
static bool comment_state = 0;


Parser :: Parser (FILE * main_dict) : D_(main_dict)
{
    current_state_ = BEGIN_STATE;
    input_file_ = NULL;
    number_of_string_ = 1;
    print_errors_ = 1;
    bad_ident_= 0;
}


Symbols Parser :: returnSymbolType (char symbol)
{
   static const string separat("?+-!=<>;, ():{}\t\r|&~.#\b");
   if ( ('A' <= symbol && symbol <= 'Z') || ('a' <= symbol && symbol <= 'z') )
      return LETTER;
   if ( '0' <= symbol && symbol <= '9')
      return DIGIT;
   if (symbol == '_')
      return UNDERLINE;
   if (symbol == '/')
      return SLASH;
   if (symbol == '*')
      return GEAR;
   if (symbol == '\'' || symbol == '\"')
      return QUOTES;
   if (separat.find(&symbol))
      return SEPARATIONS;
   if (symbol == '\n' || symbol == '\0')
      return LINE_END;
}



States Parser ::  beginState (Symbols symbol_type )
{
   while(symbol_type == DIGIT || symbol_type == GEAR || symbol_type== SEPARATIONS)
   {
     ++current_symbol_;
     symbol_type = returnSymbolType(*current_symbol_);
   }

   switch (symbol_type)
   {
     case LETTER :
     case UNDERLINE : return IN_IDENTIFIER;
     case SLASH : return BEFORE_COMMENT;
     case QUOTES : return IN_STRING;
     case LINE_END : return END;
   }
}




States Parser :: inIdentifier (Symbols symbol_type)
{
	//printf("in id\n");
   begin_identifier_ = current_symbol_;
   while(symbol_type == LETTER ||symbol_type == DIGIT || symbol_type == UNDERLINE)
   {
      symbol_type = returnSymbolType(*current_symbol_);
      ++current_symbol_;
   }
   switch (symbol_type)
   {
      case GEAR :
      case SEPARATIONS :
      	//printf("before if %d\n", print_errors_);
      	//if(print_errors_)     //странная штука
      	//{
      		//printf("in if\n");
         	workUp();
         //}
         //printf("after if\n");
         return BEGIN_STATE;
      case LINE_END :
      	if(print_errors_)
         	workUp();
         return END;
   }
}




States Parser ::  inComment (Symbols symbol_type)
{
   while(symbol_type != GEAR && symbol_type!= LINE_END)
   {
      ++current_symbol_;
      symbol_type = returnSymbolType(*current_symbol_);
   }
   switch (symbol_type)
   {
      case GEAR:
         return AFTER_COMMENT;
      case LINE_END :
         comment_state = 1;
         return END;
   }
}



States Parser :: inString (Symbols symbol_type)
{
   ++current_symbol_;
   symbol_type = returnSymbolType(*current_symbol_);
   while(symbol_type != QUOTES && symbol_type != LINE_END)
   {
      ++current_symbol_;
      symbol_type = returnSymbolType(*current_symbol_);
   }
   ++current_symbol_;
   switch (symbol_type)
   {
      case QUOTES:
         return BEGIN_STATE;
      case LINE_END :
         return END;
   }
}




States Parser :: beforeComment (Symbols symbol_type)
{
   ++current_symbol_;
   switch(returnSymbolType(*current_symbol_))
   {
      case LINE_END:
      case SLASH:
         return END;
      case GEAR:
         return IN_COMMENT;
      default:
         return BEGIN_STATE;
   }
}

States Parser :: afterComment (Symbols symbol_type)
{
   ++current_symbol_;
   switch(returnSymbolType(*current_symbol_))
   {
      case SLASH:
         return BEGIN_STATE;
      case LINE_END :
         return END;
      default:
         return IN_COMMENT;
   }
}

void Parser :: notPrintError()
{
	print_errors_ = 0;
}

set <string>& Parser :: runParser()   //ñäåëàòü ïðîâåðêó, ÷òîá áåç ôàéëà íå çàïóñêàëîñü
{
   number_of_string_ = 1;
   current_state_ = BEGIN_STATE;  //Âðåìåííàÿ øòóêà
   fgets(current_string_, MAX_STRING_SIZE, input_file_); //ñ÷èòûâàåì ñòðîêó èç ôàéëà
   current_symbol_ = current_string_;
   while (!feof(input_file_))
   {
      switch(current_state_)
      {
         case BEGIN_STATE :
         	//printf("in bs\n");
            current_state_ = beginState (returnSymbolType(*current_symbol_));
            break;
         case IN_IDENTIFIER :
         	//printf("in bs before\n");
            current_state_ =  inIdentifier (returnSymbolType(*current_symbol_));
            break;
         case IN_COMMENT :
           current_state_ =  inComment (returnSymbolType(*current_symbol_));
            break;
         case IN_STRING :
            current_state_ =  inString (returnSymbolType(*current_symbol_));
            break;
         case BEFORE_COMMENT :
            current_state_ =  beforeComment (returnSymbolType(*current_symbol_));
            break;
         case AFTER_COMMENT :
            current_state_ =  afterComment (returnSymbolType(*current_symbol_));
            break;
         case END :
            if(!comment_state)
            {
               current_state_ = BEGIN_STATE;
            }
            else
            {
               current_state_ = IN_COMMENT;
            }
            comment_state = 0;
            ++number_of_string_;
            fgets(current_string_, MAX_STRING_SIZE, input_file_); //ñ÷èòûâàåì ñòðîêó èç ôàéëà
            current_symbol_ = current_string_;
      }
   }
   if(bad_ident_/number_of_string_ > 0.3);
        printf("%s : warning : frecuent usage of short identifier's name \n", input_file_name_); //??
   return unic_words_;
}


bool Parser ::  setInputFile  (FILE * input_file, const char * file_name)
{
   input_file_ = input_file;
   string str(file_name);
   int i = 0;
   for(i; i < str.size(); ++i)
   {
       input_file_name_[i] = str[i];
   }
   input_file_name_[i] = '\0';
   return 1;
}


bool Parser :: workUp ()
{
   string word;
   vector <string> f_word;
   vector <string>  result;
   //HammingCmp cmp;
   LevensteinCmp cmp;
   while (begin_identifier_ != (current_symbol_-1))
   {
      word.push_back(*begin_identifier_);
      ++begin_identifier_;
   }
   /*if(!print_errors_)      //если мы просто составляем список идентификаторов
   {
   	unic_words_.insert(word);
   }
   else
   {*/
   	if(word.size() < 3)
   		++bad_ident_;
		if((unic_words_.find(word) == unic_words_.end()) && (word.size() > 2))
		{
		   unic_words_.insert(word);
		   size_t pos = 0;
		   while(pos < word.size())
		   {
		      f_word.push_back("");
		      bool flag_isupper = isupper(word[pos]);
		      for (pos; ((word[pos]!= '_')&&!(flag_isupper == 0 && isupper(word[pos])) == 1) && pos < word.size(); ++pos)
		      {
		         flag_isupper = isupper(word[pos]);
		         f_word.back().push_back(tolower(word[pos]));
		      }
		      if(word[pos] == '_')
		         ++pos;
		   }
		   
		   if((f_word.size() < 2) && (!D_.findInSet(f_word[0])) && (f_word[0].size() > 3))    //если слово не разбито на слова и его нет в словаре
		   {
		   	string part_word;
		   	vector <string> words;
		   	size_t q = 0;
		   	for(size_t r = q; r < (q + 3) ; ++r)           //считали первые 3 буквы
					part_word.push_back((f_word[0])[r]);
				q = q + 3;
		   	while(q < (f_word[0]).size())                  //пока не конец слова
		   	{
				   if(D_.findInSet(part_word))                //если нашли в словаре, то отрезаем часть и делаем разбивку заново
				   {
				   	words.push_back(part_word);
				   	part_word.clear();
				   	for(size_t r = q; r < (q + 3) ; ++r)
							part_word.push_back((f_word[0])[r]);
						q = q + 3; 
				   }
				   else
				   {
				   	part_word.push_back((f_word[0])[q]);
				   	++q;
				   }
		      }
		      words.push_back(part_word);
		      if(words.size() > 1)
		      {
		      	printf("%s: %d : warning : in identifier '%s' words are not separated, use CamelCase or underscore :",input_file_name_, number_of_string_,f_word[0].data());
		      	size_t i = 0;
		      	 for (i; i < words.size()-1; ++i)
		            printf("%s_", words[i].data());
		         printf("%s\n", words[i].data());
		         return 1;                                     //или выводить потом еще ошибку в этом слове?
		      }      
		      	
		   }
		   
		   for(size_t k = 0; k < f_word.size(); ++k)
		   {
		      result = D_.find(f_word[k],cmp);
		      if(result.size())
		      {
		         if(f_word.size() == 1)
		            printf("%s: %d : error : '%s' is misspelled, candidates: ", input_file_name_, number_of_string_,word.data());
		         else
		            printf("%s: %d : error : '%s' is misspelled in identifier '%s', candidates: ", input_file_name_, number_of_string_,f_word[k].data(),word.data());
		         for (size_t i = 0; i < result.size(); ++i)
		            printf("%s  ", result[i].data());
		         printf("\n");
		      }
		   }
		}
	//}
   return 1;
}
